package com.timedomain.him.remake.yggdrasil;

import android.content.Context;

import com.timedomain.him.remake.Program;
import com.timedomain.him.remake.R;
import com.timedomain.him.remake.core.ChannelCreator;
import com.timedomain.him.remake.core.SimplePlayer;
import com.timedomain.him.remake.models.ChatMessage;
import com.timedomain.him.remake.utils.AppSettings;
import com.timedomain.him.remake.utils.FileUtils;
import com.timedomain.him.remake.utils.HLog;
import com.timedomain.him.remake.utils.TimeUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 「世界树」
 * 承载了一个小小世界所有信息洪流的数据结构。
 * 如果能够悉知「他」的所有可能性，也许就能让「他」在新的世界重生吧。
 * @author ZYFDroid
 */
public class TheWorldTree {

    private Context ctx;
    public TheWorldTree(Context ctx){
        this.ctx = ctx;
        Thread loadThread = new Thread(() -> treeLeaves = WorldLoader.load(ctx));
        loadThread.start();
        try {
            loadThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        reset();
    }

    public static final char NODE_SELECTION = '-';
    public static final char NODE_SEND = '<';
    public static final char NODE_RESPONSE = '>';
    public static final char NODE_SPECIAL = ':';
    public static final char NODE_JUMP = ']';
    public static final char NODE_CALL = '[';
    public static final char NODE_FLAGADD = '+';
    public static final char NODE_FLAGDEL = 'x';
    public static final char NODE_JUMPIF = '?';

    public static final String LEAF_ENTRY="[::Begin]";
    public static final String LEAF_END="[::End]";

    public HashMap<String,TreeLeaf> treeLeaves = new HashMap<>();

    public String[] currentState = new String[128];
    public int stackPointer = 0;


    public void loadState(String state){
        String[] splited = state.split(Pattern.quote("/"));
        for (int i=0;i<splited.length;i++){
            currentState[i] = splited[i];
            stackPointer = i;
        }
    }

    public String saveState(){
        StringBuilder stackSerializer = new StringBuilder();
        for (int i = 0;i<stackPointer;i++){
            stackSerializer.append(currentState[i]);
            stackSerializer.append("/");
        }
        stackSerializer.append(currentState[stackPointer]);
        return stackSerializer.toString();
    }

    public void reset(){
        stackPointer = 0;
        currentState[stackPointer] = LEAF_ENTRY;
    }

    public void nextTick() throws YieldChatMessage,YieldSelection{
        boolean exitLoop = false;
        int loopedSteps = 0;
        do {
            loopedSteps++;
            TreeLeaf currentLeaf = treeLeaves.get(currentState[stackPointer]);
            HLog.i("WorldTreeUpdate", "CurrentLeaf: "+currentLeaf.tagName);
            if(currentLeaf == null){
                HLog.e("Ticker", "nextTick: Cannot found next leaf: "+currentState[stackPointer]);
                return;
            }

            // 遇到返回的情况
            if(currentLeaf.branches.size() == 0){
                if(stackPointer > 0){
                    stackPointer--;
                    continue;
                }
                else{
                    exitLoop = true;
                    break;
                }
            }

            if(currentLeaf.branches.size() == 1){
                String line = currentLeaf.branches.get(0);
                char type = line.charAt(0);
                String stripped = extractLineContent(line);
                // 只有一个跳转的情况
                if(type == NODE_JUMP){
                    evalLine(LEAF_END,line);
                }
                // 只有一个条件跳转的情况
                else if(type == NODE_JUMPIF){
                    if(checkCondition(stripped)){
                        evalLine(LEAF_END,line);
                    }
                    else{
                        currentState[stackPointer] = LEAF_END;
                    }
                }
                else{
                    // 只有一个非跳转的情况

                    if(type == NODE_SELECTION){
                        evalSelection(new String[]{line});
                    }
                    else{

                        evalLine(LEAF_END,line);
                    }
                }
            }
            else {

                // 全都是跳转的情况
                if(getType(currentLeaf.branches.get(0)) == NODE_JUMP && getType(currentLeaf.branches.get(currentLeaf.branches.size()-1)) == NODE_JUMP){
                    int which = randInt(currentLeaf.branches.size());
                    evalLine(LEAF_END,currentLeaf.branches.get(which));
                }
                // 尾部有跳转的情况
                else if(getType(currentLeaf.branches.get(currentLeaf.branches.size()-1)) == NODE_JUMP){
                    String tailLine = currentLeaf.branches.get(currentLeaf.branches.size()-1);
                    String tailState = tailLine.substring(2);
                    // 是条件跳转
                    if(getType(currentLeaf.branches.get(0)) == NODE_JUMPIF){
                        List<String> matchedCondition = new ArrayList<>();
                        for (int i = 0;i<currentLeaf.branches.size()-1;i++){
                            if(checkCondition(currentLeaf.branches.get(i).substring(2))){
                                matchedCondition.add(currentLeaf.branches.get(i));
                            }
                        }
                        if(matchedCondition.size() == 0){
                            evalLine(LEAF_END,tailLine);
                        }
                        else{
                            int which = randInt(matchedCondition.size());
                            evalLine(tailState,matchedCondition.get(which));
                        }

                    }
                    // 不是条件跳转
                    else{
                        int which = randInt(currentLeaf.branches.size()-1);
                        evalLine(tailState,currentLeaf.branches.get(which));
                    }
                }
                // 尾部没有跳转的情况
                else{
                    if(currentLeaf.branches.get(0).charAt(0) == NODE_SELECTION){
                        evalSelection(currentLeaf.branches.toArray(new String[0]));
                    }
                    else{
                        int which = randInt(currentLeaf.branches.size());
                        evalLine(LEAF_END,currentLeaf.branches.get(which));
                    }
                }
            }



        }while (!exitLoop && loopedSteps < 512);

        if(loopedSteps > 256){
            HLog.w("Ticker", "nextTick: A single tick took too many steps. "+loopedSteps );
        }




    }

    private void evalLine(String nextState,String line) throws YieldSelection,YieldChatMessage{
        char type = line.charAt(0);
        String stripped = extractLineContent(line);
        currentState[stackPointer] = nextState;

        switch (type){
            case NODE_CALL:
                stackPointer++;
                currentState[stackPointer] = stripped;
                break;
            case NODE_JUMP:
                currentState[stackPointer] = stripped;
                break;

            case NODE_FLAGADD:
                AppSettings.worldTreeFlags.add(stripped);
                break;
            case NODE_FLAGDEL:
                AppSettings.worldTreeFlags.remove(stripped);
                break;
            case NODE_JUMPIF:
                String jumpTarget = stripped.split(Pattern.quote(" "))[1];
                currentState[stackPointer] = jumpTarget;
                break;
            case NODE_SEND:
                doSend(stripped);
                break;
            case NODE_RESPONSE:
                doResponse(stripped);
                break;
            case NODE_SPECIAL:
                doSpecial(stripped);
        }

    }

    private void evalSelection(String[] lines) throws YieldSelection {
        ArrayList<Selection> selections = new ArrayList<>();
        for (String line :
                lines) {
            String[] param = line.substring(2).split(Pattern.quote("->"));
            Selection selection = new Selection();
            selection.buttonTitle = param[0].trim();
            String targetState = param[1].trim();
            selection.sendingMessage = pullSelectionResponse(targetState);
            selection.nextLeaf = pullSelectionTarget(targetState);
            selections.add(selection);

        }
        throw new YieldSelection(selections);
    }
    public void applySelection(Selection sel) throws YieldChatMessage {
        currentState[stackPointer] = sel.nextLeaf;
        doSend(sel.sendingMessage);
    }

    private String pullSelectionResponse(String targetState){
        TreeLeaf selectionTarget = treeLeaves.get(targetState);
        assert selectionTarget != null;
        int which = randInt(selectionTarget.branches.size()-1);
        return selectionTarget.branches.get(which).substring(2);
    }
    private String pullSelectionTarget(String targetState){
        TreeLeaf selectionTarget = treeLeaves.get(targetState);
        assert selectionTarget != null;
        return selectionTarget.branches.get(selectionTarget.branches.size()-1).substring(2);
    }

    private void doSend(String stripedLine) throws YieldChatMessage {
        ChatMessage chatMessage = new ChatMessage(TimeUtils.getTimeStampIgnoreTimeZone(),deformat(stripedLine),ChatMessage.TYPE_SEND_MESSAGE,"","");
        throw new YieldChatMessage(chatMessage);
    }


    private void doResponse(String stripedLine) throws YieldChatMessage {
        ChatMessage chatMessage = new ChatMessage(TimeUtils.getTimeStampIgnoreTimeZone(),"",ChatMessage.TYPE_MESSAGE,null,"");
        if(stripedLine.startsWith("{")){
            int endIndex = stripedLine.indexOf('}');
            String audioFile = stripedLine.substring(1,endIndex);
            String contentLine = stripedLine.substring(endIndex+1);
            String audioFilePath = FileUtils.pathCombine("chat","voice",audioFile);
            chatMessage.audioFile = audioFilePath;
            try(SimplePlayer sp = new SimplePlayer(ChannelCreator.FromFile(ctx,audioFilePath))){
                chatMessage.extra = ((sp.getDuration() + 999) / 1000)+"秒";
            }
            chatMessage.content = deformat(contentLine);
        }
        else{
            chatMessage.audioFile = "";
            chatMessage.content = deformat(stripedLine);
        }
        throw new YieldChatMessage(chatMessage);
    }

    private static String specialTelephone = "[Telephone:";
    private static String specialVoice = "[Voice:";
    private static String specialShare = "[Share:";
    private static String specialNight = "[Night]";
    private static String specialClearTime = "[UnsetTime]";

    private void doSpecial(String stripedLine) throws YieldChatMessage {
        ChatMessage chatMessage = new ChatMessage(TimeUtils.getTimeStampIgnoreTimeZone(),"",ChatMessage.TYPE_SEND_MESSAGE,"","");
        if(stripedLine.startsWith(specialTelephone)){
            String gotPath = extractPath(stripedLine,specialTelephone);
            chatMessage.audioFile = gotPath;
            chatMessage.content = FileUtils.getFileNameWithoutExtension(gotPath);
            chatMessage.type = ChatMessage.TYPE_PHONE_CALL;
        }
        if(stripedLine.startsWith(specialVoice)){
            String gotPath = extractPath(stripedLine,specialVoice);
            chatMessage.audioFile = gotPath;
            chatMessage.content = FileUtils.getFileNameWithoutExtension(gotPath);
            chatMessage.type = ChatMessage.TYPE_VOICE;
        }
        if(stripedLine.startsWith(specialShare)){
            String gotPath = extractPath(stripedLine,specialShare);
            chatMessage.audioFile = gotPath;
            chatMessage.content ="[分享文件] "+ FileUtils.getFileNameWithoutExtension(gotPath)+".mp3";
            chatMessage.type = ChatMessage.TYPE_SHARE_SONG;
        }
        if(stripedLine.startsWith(specialNight)){
            chatMessage.type = ChatMessage.TYPE_START_NIGHT_MODE;
        }
        if(stripedLine.startsWith(specialClearTime)){
            chatMessage.type = ChatMessage.TYPE_CLEAR_MORNING_TIME;
        }
        throw new YieldChatMessage(chatMessage);
    }

    private String extractPath(String specialStr,String specialPrefix){
        specialStr = specialStr.substring(specialPrefix.length());
        specialStr = specialStr.substring(0,specialStr.length()-1);
        String path = specialStr;
        if(!Program.getDataPack(null).containsKey(path)){
            return FileUtils.pickRandomOneOgg(path);
        }
        else{
            return path;
        }
    }


    private String extractLineContent(String line){
        return line.substring(2);
    }

    private boolean checkCondition(String strippedLine){
        return AppSettings.worldTreeFlags.contains(strippedLine.split(Pattern.quote(" "))[0]);
    }

    private char getType(String line){
        return line.charAt(0);
    }

    private int randInt(int arrayLength){
        int n = (int) (Math.random() * arrayLength);
        if(n >= arrayLength){
            n = arrayLength - 1;
        }
        return n;
    }

    private static final String guidThatMayNeverAppearsInNormalText = "{aisdbweaW-ACBA-293HNFV-ACOUBAIR-ASOD81}";

    private static String deformat(String formated){
        // 文本中可能有换行。用\n表示换行，用\\表示\
        formated = formated.replace("\\\\",guidThatMayNeverAppearsInNormalText);
        formated = formated.replace("\\n","\n");
        formated = formated.replace(guidThatMayNeverAppearsInNormalText,"\\");
        return formated;
    }

}
